package io.nio.groupchat;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

/**
 * 群聊服务器端
 *
 * @author Verlet
 * @date 2023-01-07 15:25
 */
@Slf4j
public class GroupChatServer {

    private static final Integer DEFAULT_PORT = 6728;

    private Selector selector;

    private final ByteBuffer readBuf = ByteBuffer.allocate(1024 * 4);
    private final ByteBuffer writeBuf = ByteBuffer.allocate(1024 * 4);

    public GroupChatServer(Integer port) {
        try {
            selector = Selector.open();

            ServerSocketChannel ssc = ServerSocketChannel.open();
            ssc.configureBlocking(false);
            ssc.bind(new InetSocketAddress(port));
            ssc.register(selector, SelectionKey.OP_ACCEPT);

            log.info("群聊服务端启动成功，端口: {}", port);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void start() {
        while (true) {
            try {
                selector.select();
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    iterator.remove();
                    // 是否有效
                    if (selectionKey.isValid()) {
                        if (selectionKey.isAcceptable()) {
                            accept(selectionKey);
                        } else if (selectionKey.isReadable()) {
                            read(selectionKey);
                        } else {
                            log.info("selectionKey ==> {}", selectionKey);
                        }
                    }
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void read(SelectionKey selectionKey) {
        readBuf.clear();
        SocketChannel sc = (SocketChannel) selectionKey.channel();
        try {
            int count = sc.read(readBuf);
            if (count == -1) {
                selectionKey.channel().close();
                selectionKey.cancel();
                return;
            }
            readBuf.flip();
            byte[] bytes = new byte[this.readBuf.remaining()];
            this.readBuf.get(bytes);
            String body = new String(bytes).trim();
            if (StringUtils.isBlank(body)) {
                return;
            }
            log.info("服务端接收到 {} 的数据 ==> {}", sc.getRemoteAddress(), body);
            String hostname = null;
            // 实现单聊功能 @hostname msg
            if (body.startsWith("@")) {
                // 单聊功能
                hostname = body.substring(1, body.indexOf(" "));
                body = body.substring(body.indexOf(" ") + 1);
            }
            String msg = sc.getRemoteAddress() + " ==> " + body;


            // 发送到连接的客户端
            Iterator<SelectionKey> iterator = selector.keys().iterator();
            while (iterator.hasNext()) {
                SelectionKey sk = iterator.next();
                SelectableChannel targetChannel = sk.channel();
                if (targetChannel instanceof SocketChannel channel && channel != sc) {
                    // 单聊功能，目标channel不是当前channel
                    if (hostname != null && !hostname.equals(channel.getRemoteAddress().toString().substring(1))) {
                        continue;
                    }
                    writeBuf.clear();
                    writeBuf.put(msg.getBytes());
                    writeBuf.flip();
                    channel.write(writeBuf);
                    log.info("服务端转发到 {} ==> {}", channel.getRemoteAddress(), msg);
                }
            }

        } catch (SocketException se) {
            try {
                log.info("{} 下线了。。。当前在线人数为 {}", ((SocketChannel) selectionKey.channel()).getRemoteAddress(), selector.keys().size() - 2);
                selectionKey.channel().close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            selectionKey.cancel();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private void accept(SelectionKey selectionKey) {
        ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel();
        try {
            SocketChannel sc = ssc.accept();
            sc.configureBlocking(false);
            sc.register(selector, SelectionKey.OP_READ);
            log.info("{} 上线了。。。当前在线人数为 {}", sc.getRemoteAddress(), selector.keys().size() - 1);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Integer port = null;
        if (args.length > 1 && StringUtils.isNotBlank(args[1])) {
            port = Integer.valueOf(args[1]);
        }
        if (port == null) {
            port = DEFAULT_PORT;
        }
        GroupChatServer groupChatServer = new GroupChatServer(port);
        groupChatServer.start();

    }
}
